package com.tomtop.core;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mobile.device.Device;
import org.springframework.mobile.device.DeviceUtils;
import org.springframework.util.StringUtils;

import com.tomtop._const.Const;
import com.tomtop.core.model.SysUser;
import com.tomtop.core.utils.ContextUtils;

/**
 * 操作上下文
 * @author Administrator
 * 请勿在service层使用 
 */
public final class Context {
	private final static ThreadLocal<Context> contextHolder = new ThreadLocal<Context>(){protected Context initialValue() {return new Context();};};
	
	private static Logger logger = LoggerFactory.getLogger(Context.class);
	
	private Map<String, Object> contextMap ;
	
	private HttpServletResponse response;
	
	private HttpServletRequest request;
	
	/**
	 * 当前线程中得消息
	 */
	private String message=null;
	/**
	 * 处理请求开始的时间,毫秒，
	 */
	private Long beginTime=null;
	
	/**
	 * 当前操作的标题
	 */
	private String 	title=null;
	private String clientid=null;
	private String host;
	private String callback=null;
	/**
	 * 当前登录的用户，如果存在(线程用户 )
	 */
	private Optional<SysUser> user=null;
	private String hostname=null;
	private String requestUri=null;
	private String userid=null;
	private Boolean ispc=null;
	private Boolean isapp=null;
	private Boolean isRest=false;
	
	public static Context getContext(){
		return contextHolder.get();
	}
	public static Context init(HttpServletRequest request,HttpServletResponse response){
		if(request==null)return null;
		Context context=getContext();
		context.request=request;
		context.response=response;
		context.beginTime=System.currentTimeMillis();
		context.host = request.getHeader("Host");
		context.clientid=null;
		context.user=null;
		context.hostname=null;
		context.requestUri=null;
		context.userid=null;
		context.ispc=null;
		context.isapp=null;
		context.isRest=false;
		return context;
	}
	public static boolean isPc() {
		Context context=getContext();
		if(context.ispc!=null)
			return context.ispc;
		if(context.request==null)
			return context.ispc=true;
		Device currentDevice = DeviceUtils.getCurrentDevice(context.request);
		return getContext().ispc=(currentDevice.isNormal()&&(context.host==null || !context.host.toLowerCase().startsWith("m")));
	}
	public static boolean isLogin() {
		Optional<SysUser> user= getUser();
		return user.isPresent()&&user.get().isLogin();
	}
	
	public void setUserid(String userid) {
		this.userid = userid;
	}
	
	public static void sendUserId(){
		String userid = Context.getCookie(Const.USERID_COOKIE_NAME);
		if(userid==null){
			userid=UUID.randomUUID().toString().replaceAll("-", "");
			Context.addCookie(Const.USERID_COOKIE_NAME, userid);
		}
	}
	
	public static String getCallback() {
		Context context=getContext();
		if(context.callback==null&&context.request!=null)
			context.callback=context.request.getParameter("jsoncallback");
		return context.callback;
	}
	public static String getHostName() {	
		Context context=getContext();
		if(context.hostname!=null)
			return context.hostname;
		int endIndex  ;
		int beginIndex  ;
		if(	context.host!=null
			&&
			0<(endIndex = context.host.lastIndexOf('.'))
			&&
			0<=(beginIndex = context.host.lastIndexOf('.',endIndex-1))
			&&
			1<(endIndex-beginIndex)
		)
			return (context.hostname=context.host.substring(beginIndex+1, endIndex));
		return (context.hostname="");
	}

	public static String getHost() {
		return getContext().host;
	}
	
	public static void isRest(boolean isResEntity) {
		getContext().isRest = isResEntity;
	}
	
	public static boolean isRest() {
		return getContext().isRest;
	}
	
	private final static String error_uri_key="javax.servlet.error.request_uri";
	public static String getRequestUri() {
		Context context= getContext();
		if(context.requestUri!=null)
			return context.requestUri;
		if(context.request==null)
			return context.requestUri="";
		String error_uri=(String) context.request.getAttribute(error_uri_key);
		if(error_uri!=null)
			return (context.requestUri=error_uri);
		else
			return (context.requestUri=context.request.getRequestURI());
	}
	
	public static void remove(){
//		String title=getTitle();
//		String message=getMessage();
		contextHolder.remove();
	}
	
	public static Optional<SysUser> getUser() {
		Context context=getContext();
		if(context.user!=null)
			return context.user;
		if(context.request==null)
			return context.user=SysUser.now();
		return ContextUtils.initLoginInfo();
	}
	public static boolean isApp() {
		Context context=getContext();
		if(context.isapp!=null) 
			return context.isapp;
		return context.isapp=ContextUtils.isApp();
	}
	public static void setUser(Optional<SysUser> user) {
		getContext().user = user;
	}
	public static long getBeginTime() {
		return getContext().beginTime;
	}

	public static String getMessage() {
		return getContext().message;
	}

	public static void setMessage(String message) {
		getContext().message = message;
	}

	public static String getTitle() {
		return getContext().title;
	}

	public static void setTitle(String titleformat,Object ... arg1) {
		getContext().title = String.format(titleformat, arg1);
	}

	public static void setRequest(HttpServletRequest request) {
		getContext().request = request;
	}

	public static void setResponse(HttpServletResponse response) {
		getContext().response = response;
	}
	public static HttpServletRequest getRequest() {
		return getContext().request;
	}
	public static HttpSession getSession() {
		HttpServletRequest request=getRequest();
		if(request==null)
			return null;
		for (int i = 0; i < 3; i++) 
			try { return request.getSession(); } catch (Exception e) { e.printStackTrace(); }
		return null;
		
	}
	public static HttpServletResponse getResponse() {
		return getContext().response;
	}
	/**
	 * 设置值全部设置的request
	 * @param key
	 * @param value
	 */
	public static void put(String key,Object value){
		Context context=getContext();
		if(context.request!=null)
			context.request.setAttribute(key, value);
		else{
			if(context.contextMap==null)
				context.contextMap=new HashMap<>();
			context.contextMap.put(key, value);
		}
	}
	
	/**
	 * 首先从request中取，取不到从请求参数中取，继续道请求头中取，继续到会话中取，继续到ServletContext中取,继续到redis中取,如果依然取不到，就返回null
	 * @param key
	 * @return
	 */
	public static Object get(String key){
		Context context=getContext();
		Object value=null;
		if(context.request!=null&&(value=context.request.getAttribute(key))!=null)
			return value;
		if(context.contextMap!=null&&(value=context.contextMap.get(key))!=null)
			return value;
		if(context.request==null)
			return value;
		if((value=context.request.getParameter(key))!=null)
			return value;
		if((value=context.request.getHeader(key))!=null)
			return value;
		HttpSession session = context.request.getSession();
		if((value=session.getAttribute(key))!=null)
			return value;
		return getCookie(key);
	}
	
	public static String getCookie(String name) {
		if(StringUtils.isEmpty(name)){
			logger.info("Cookie Name 为空 ：CookieName={}",name);
			return null;
		}
		HttpServletRequest request=getRequest();
		Cookie[] cookies = request.getCookies();
		if(cookies==null){
			logger.info("没有取到 Cookie ：CookieName={}",name);
			return null;
		}
		for (Cookie cookie : cookies) {
			if(name.equals(cookie.getName()))
				try {
					String cookie_value= cookie.getValue();
					cookie_value=URLDecoder.decode(cookie_value, Const.DEFAULT_CHARSET.name());
					logger.debug(" Cookie中取到值 ：CookieName={},CookieValue={}",name,cookie_value);
					return cookie_value;
				} catch (UnsupportedEncodingException e) {
					logger.warn("读取cookie name={},出现异常",name, e);
				}
		}

		logger.debug(" Cookie中没有取到值 ：CookieName={}",name);
		return null;
	}
	
	public static void addCookie(String name,String value){
		Context.addCookie(name, value, null, null, null, null);
	}
	
	public static void addCookie(String name,String value,String domain){
		Context.addCookie(name, value, domain, null, null, null);
	}
	
	public static void addCookie(String name,String value,String domain,Integer maxage){
		Context.addCookie(name, value, domain, maxage, null, null);
	}
	
	public static void addCookie(String name,String value,String domain,Integer maxage,Boolean httpOnly,Integer version){
		if(StringUtils.isEmpty(name)||StringUtils.isEmpty(value)){
			return ;
		}
		HttpServletResponse response=getResponse();
		try {
			Cookie cookie = new Cookie(name, URLEncoder.encode(value,Const.DEFAULT_CHARSET.name()));
			if(domain!=null||(domain=getDefaultDomain())!=null)
				cookie.setDomain(domain);
			if(maxage==null)
				maxage=Integer.MAX_VALUE;
			cookie.setMaxAge(maxage);
			if(httpOnly!=null)
				cookie.setHttpOnly(httpOnly);
			if(version!=null)
				cookie.setVersion(version);
			cookie.setPath("/");
			response.addCookie(cookie);
			
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
	}
	private static String getDefaultDomain() {
		String host=Context.getHost();
		String hostName=Context.getHostName();
		if(host!=null&&hostName!=null)
			return host.substring(host.lastIndexOf(hostName+"."));
		return null;
	}
	public static void delCookie(String name){
		delCookie(name, null);
	}
	
	public static void delCookie(String name,String domain){
		if(name==null)return;
		if(domain==null&&(domain=getDefaultDomain())==null)return;
		HttpServletResponse response=getResponse();
		logger.debug("即将从cookie删除{},",name);
		Cookie delcookie=new Cookie(name, "");
		delcookie.setPath("/");
		if(domain!=null)
			delcookie.setDomain(domain);
		delcookie.setMaxAge(0);
		response.addCookie(delcookie);
	}
	/***
	 * 获取客户端IP地址;这里通过了Nginx获取;X-Real-IP,
	 * @return
	 */
	private final static String[] keys=new String[]{"X-Forwarded-For","X-Real-IP","Proxy-Client-IP","WL-Proxy-Client-IP"};
	public static String getClientIP() {
		HttpServletRequest request=getRequest();
		if(request==null)return null;
		for (String key : keys) {
			String ip = request.getHeader(key);
			if(StringUtils.hasText(ip)&&!"unknown".equalsIgnoreCase(ip))
				return ip.split(",")[0];
		}
		return request.getRemoteAddr();
	}
	/**
	 * 获取请求的客户端ip链路
	 * @return
	 */
	public static String getClientIPLink() {
		HttpServletRequest request=getRequest();
		if(request==null)return null;
		return request.getHeader(keys[0]);
	}

	/**
	 * 客户端转发至redirecturi
	 */
	public static void sendRedirect(String redirecturi) {
		logger.info("请求转发至"+redirecturi);
		HttpServletResponse response=getResponse();
		if(response==null)return;
		try {
			response.sendRedirect(redirecturi);
		} catch (IOException e) {
			logger.warn("请求转发至"+redirecturi+"出现异常",e);
		}
	}

	/**
	 * 服务器端转发至
	 * @param forwarduri
	 */
	public static void forward(String forwarduri) {
		logger.info("请求转发至"+forwarduri);
		HttpServletRequest request=getRequest();
		if(request==null)return;
		try {
			request.getRequestDispatcher(forwarduri).forward(request, getResponse());
		} catch (IOException | ServletException e) {
			logger.warn("请求转发至"+forwarduri+"出现异常",e);
		}
	}
	
	public static String getUserId() {
		if(isLogin())
			return getUser().get().getUserId();
		String userid=getContext().userid;
		if(userid!=null)
			return userid;
		return getCookie(Const.USERID_COOKIE_NAME);
	}
	
	public static String getClientid() {
		Context context=getContext();
		if(context.clientid==null)
			context.clientid=context.request.getHeader("client");
		return context.clientid;
	}
	
	public static void setClientId(String clientId){
		getContext().clientid = clientId;
	}
	
	public static String getScheme() {
		return getRequest().getScheme();
	}
	
	@Override
	public String toString() {
		return new StringBuffer("\r\n请求上下文信息  \r\n")
				.append(" 请求地址:")
				.append(getHost())
				.append(getRequestUri())
				.append(" 请求来源:")
				.append(get("Referer"))
				.append(" 客户端信息:")
				.append(get("User-Agent"))
				.append(" 客户端编号:")
				.append(getClientid())
				.append(" 客户端IP:")
				.append(getClientIP())
				.append(" 客户端ip链路:")
				.append(getClientIPLink())
				.append(" 上下文描述信息:")
				.append(this.message).toString();
	}
}
